

let wrapCallback = function (callback, log, methodName) {
	callback = callback || function () { };
	let start = new Date().getTime();
	return (err, result) => {
		log.runLog('info', methodName, 'ObsClient cost ' + (new Date().getTime() - start) + ' ms');
		if (Object.prototype.toString.call(err) === '[object String]') {
			return callback(new Error(err), result);
		}

		return callback(err, result);
	};
};




let checkPrefix = function (prefix) {
	if (Object.prototype.toString.call(prefix) === '[object String]' && prefix.lastIndexOf('/') !== prefix.length - 1) {
		prefix += '/';
	}
	return prefix;
};

let posix = {};
posix.extend = function (ObsClient) {
	ObsClient.prototype.dropFile = function (param, callback) {
		this.deleteObject(param, callback);
	};

	ObsClient.prototype.dropFolder = function (param, callback) {
		let that = this;
		param = param || {};
		let _callback = wrapCallback(callback, that.log, 'dropFolder');
		let eventCallback = wrapEventCallback(param.EventCallback);
		let taskNum = param.TaskNum || 1;
		let runningTask = 0;
		let taskQueue = [];

		let doNext = function () {
			while (runningTask < taskNum && taskQueue.length > 0) {
				taskQueue.shift()();
			}
		};

		let doDropOne = function (key, ctx, done, dryRun) {
			if (dryRun) {
				ctx.finished++;
				done(ctx);
				return;
			}

			let task = function () {
				runningTask++;
				that.dropFile({
					Bucket: ctx.bucket,
					Key: key,
				}, (err, result) => {
					runningTask--;
					ctx.finished++;
					doNext();
					if (err) {
						eventCallback('dropFileFailed', key, err);
						ctx.subDeleted = false;
					} else if (result.CommonMsg.Status >= 300) {
						eventCallback('dropFileFailed', key, result);
						ctx.subDeleted = false;
					} else {
						eventCallback('dropFileSucceed', key, result);
					}
					done(ctx);
				});
			};

			if (runningTask < taskNum) {
				task();
			} else {
				taskQueue.push(task);
			}
		};

		let delimiter = '/';

		let createDone = function (subFolder, ctx, done) {
			return subCtx => {
				if (!subCtx.isTruncated && subCtx.finished === subCtx.total && subCtx.subDeleted) {
					doDropOne(subFolder, ctx, done, false);
				}
			};
		};

		let recursiveDropByFolder = function (ctx, bucket, prefix, marker, done) {
			runningTask++;
			that.listObjects({
				Bucket: bucket,
				Prefix: prefix,
				Delimiter: delimiter,
				Marker: marker,
			}, (err, result) => {
				runningTask--;
				if (err) {
					_callback(err);
					return
				}

				if (result.CommonMsg.Status >= 300) {
					_callback(null, result);
					return
				}

				ctx.total += result.InterfaceResult.Contents.length;
				ctx.total += result.InterfaceResult.CommonPrefixes.length;
				if (ctx.total === 0) {
					done(ctx);
					return;
				}

				ctx.isTruncated = result.InterfaceResult.IsTruncated === 'true';

				let createRecursiveDropByFolderTask = function (subFolder) {
					return () => {
						recursiveDropByFolder({ total: 0, finished: 0, isTruncated: false, bucket: bucket, subDeleted: true }, bucket, subFolder, null, createDone(subFolder, ctx, done));
					};
				};

				for (let j = 0; j < result.InterfaceResult.CommonPrefixes.length; j++) {
					let subFolder = checkPrefix(result.InterfaceResult.CommonPrefixes[j].Prefix);
					if (runningTask < taskNum) {
						recursiveDropByFolder({ total: 0, finished: 0, isTruncated: false, bucket: bucket, subDeleted: true }, bucket, subFolder, null, createDone(subFolder, ctx, done));
					} else {
						taskQueue.push(createRecursiveDropByFolderTask(subFolder));
					}
				}

				for (let j = 0; j < result.InterfaceResult.Contents.length; j++) {
					let key = result.InterfaceResult.Contents[j].Key;
					doDropOne(key, ctx, done, key.lastIndexOf(delimiter) === key.length - 1);
				}

				if (ctx.isTruncated) {
					if (runningTask < taskNum) {
						recursiveDropByFolder(ctx, bucket, prefix, result.InterfaceResult.NextMarker, done);
					} else {
						taskQueue.push(() => {
							recursiveDropByFolder(ctx, bucket, prefix, result.InterfaceResult.NextMarker, done);
						});
					}
				}
			});
		};

		let folder = checkPrefix(param.Prefix);
		recursiveDropByFolder({ total: 0, finished: 0, isTruncated: false, bucket: param.Bucket, subDeleted: true }, param.Bucket, folder, null, (ctx) => {
			if (ctx.isTruncated || ctx.finished !== ctx.total) {
				return;
			}

			if (ctx.subDeleted) {
				that.dropFile({
					Bucket: ctx.bucket,
					Key: folder
				}, (err, result) => {
					if (err) {
						eventCallback('dropFileFailed', folder, err);
						return _callback(err);
					}

					if (result.CommonMsg.Status >= 300) {
						eventCallback('dropFileFailed', folder, result);
						return _callback(null, result);
					}
					eventCallback('dropFileSucceed', folder, result);
					return _callback(null, result);
				});
				return;
			}
			let errMessage = 'drop folder ' + folder + ' failed due to child file deletion failed';
			eventCallback('dropFileFailed', folder, new Error(errMessage));
			_callback(errMessage);
		});

	};
};

export default posix;